package com.lujianfei.filetransfer

import android.os.Build
import android.util.Log
import com.google.gson.Gson
import com.lujianfei.filetransfer.model.CustomModelBase
import java.net.Inet4Address
import java.net.NetworkInterface
import java.net.ServerSocket
import java.net.Socket
import java.util.concurrent.Executors


/**
 * Author: lujianfei
 * Date: 2023/10/16 15:09
 * Description:
 */
data class SocketClientItem(val name:String, val ipaddress: String, val content:String)
class TcpManager private constructor() {

    companion object {
        const val TAG = "TcpManager="
        const val port = 8240

        val instance by lazy { TcpManager() }
    }


    private val threadPool by lazy { Executors.newCachedThreadPool() }

    // 服务端
    private var serverSocket:ServerSocket ?= null
    private var serverRunning = false
    private val mapIpClient = mutableMapOf<String, Socket>()
    private val mapIpName = mutableMapOf<String, String>()
    private val mapIpContent = mutableMapOf<String, String>()
    fun getClientList():List<SocketClientItem> {
        val mSocketClientItems = arrayListOf<SocketClientItem>()
        mapIpClient.forEach {
            val name = if (mapIpName.containsKey(it.key)) mapIpName[it.key]?:"" else it.key
            val content = if (mapIpContent.containsKey(it.key)) mapIpContent[it.key]?:"" else ""
            mSocketClientItems.add(SocketClientItem(name = name, ipaddress = it.key, content = content))
        }
        return mSocketClientItems
    }
    private val listeners = arrayListOf<TcpMessageListener>()

    // 客户端
    private var client:Socket ?= null

    fun addListener(listener: TcpMessageListener) {
        listeners.add(listener)
    }
    fun removeListener(listener: TcpMessageListener) {
        listeners.remove(listener)
    }
    fun startServer() {
        if (serverRunning) {
            return
        }
        serverSocket = ServerSocket(port)
        serverRunning = true
        notifyListener {
            it.onServerStart(address = localIpAddress(), port = port)
        }
        if (!threadPool.isShutdown) {
            threadPool.submit {
                while (serverRunning) {
                    log("Listening to $port")
                    val acceptClient = serverSocket?.accept()?:continue
                    log("remoteSocketAddress:${acceptClient.remoteSocketAddress}")
                    log("inetAddress:${acceptClient.inetAddress}")
                    log("localAddress:${acceptClient.localAddress}")
                    log("localSocketAddress:${acceptClient.localSocketAddress}")
                    log("reuseAddress:${acceptClient.reuseAddress}")
                    /**
                     * remoteSocketAddress 192.168.1.163:63177（客户端）
                     * inetAddress:/192.168.1.163 （客户端）
                     * localAddress:/192.168.1.184 (服务端)
                     * localSocketAddress:/192.168.1.184:8240 (服务端)
                     */
                    acceptClient.remoteSocketAddress?.toString()?.let { hostAddressAndPort->
                        mapIpClient[hostAddressAndPort] = acceptClient
                        notifyListener(eachCallback = {
                            it.onClientAccept(hostAddressAndPort)
                        })
                    }

                    val name = "${Build.MANUFACTURER} ${Build.BRAND} ${Build.MODEL}"
                    sendSocketName(socket = acceptClient, type = "name", value = name)

                    threadPool.submit readClient@ {
                        val inputStream = acceptClient.getInputStream()?:return@readClient
                        // 准备一个缓冲区
                        val buffer = ByteArray(1024)
                        var len = inputStream.read(buffer)
                        while (len > 0) {
                            val ipAddress = acceptClient.remoteSocketAddress.toString()
                            val content = String(buffer, 0, len)
                            TcpMessageParser.parse(message = content, onNameAccept = { name->
                                mapIpName[ipAddress] = name
                                notifyListener(eachCallback = {
                                    it.onNameAccept(ipAddress, name)
                                })
                            }, onMessageAccept = { message->
                                mapIpContent[ipAddress] = message
                                notifyListener(eachCallback = {
                                    it.onMessageReceive(ipAddress, message)
                                })
                            })
                            len = inputStream.read(buffer)
                        }
                        mapIpClient.remove(acceptClient.remoteSocketAddress?.toString())
                        notifyListener(eachCallback = {
                            it.onClientClose(acceptClient.remoteSocketAddress?.toString()?:"")
                        })
                    }
                }
                log("Server $port close")
            }
        }
    }

    var targetAddress = ""
    fun connect(serviceNo:String) {
        if (!threadPool.isShutdown) {
            threadPool.submit {
                kotlin.runCatching {
                    targetAddress = "${getIpAddressPrefix()}.$serviceNo"
                    Log.d(TAG, "ipAddress:$targetAddress port:$port")
                    client = Socket(targetAddress, port)
                    Log.d(TAG, "connect onSuccess")
                    notifyListener { item-> item.onConnectSuccess(targetAddress) }
                    // 阻塞等待消息
                    val inputStream = client?.getInputStream()?:return@submit
                    // 准备一个缓冲区
                    val buffer = ByteArray(1024)
                    var len = inputStream.read(buffer)
                    while (len > 0) {
                        notifyListener { item-> item.onMessageReceive(localIpAddress(), String(buffer, 0, len)) }
                        len = inputStream.read(buffer)
                    }
                }.onFailure {
                    Log.d(TAG, "connect failed $it")
                    if (doStopClient) {
                        doStopClient = false
                        return@onFailure
                    }
                    notifyListener { item-> item.onConnectFailed(localIpAddress(), "$it") }
                }
            }
        }
    }

    fun sendName(name:String, onSuccess:(()->Unit)?= null, onFailed:((String)->Unit)?= null) {
        sendSocketName(socket = client, type = CustomModelBase.TYPE_NAME, value = name, onSuccess = onSuccess, onFailed = onFailed)
    }

    fun sendMessage(message:String, onSuccess:(()->Unit)?= null, onFailed:((String)->Unit)?= null) {
        sendSocketName(socket = client, type = CustomModelBase.TYPE_MSG, value = message,  onSuccess = onSuccess, onFailed = onFailed)
    }

    fun sendMessageByServer(ipaddress:String, message:String, onSuccess:(()->Unit)?= null, onFailed:((String)->Unit)?= null) {
        mapIpClient[ipaddress]?.let {
            sendSocketName(socket = it, type = CustomModelBase.TYPE_MSG, value = message,  onSuccess = onSuccess, onFailed = onFailed)
        }
    }

    private fun sendSocketName(socket: Socket?, type:String, value:String, onSuccess:(()->Unit)?= null, onFailed:((String)->Unit)?= null) {
        if (!threadPool.isShutdown) {
            threadPool.submit {
                kotlin.runCatching {
                    val sendBytes = CustomModelBase(type = type, value = value)
                    socket?.getOutputStream()?.let {
                        it.write(Gson().toJson(sendBytes).toByteArray())
                        it.flush()
                    }
                    onSuccess?.invoke()
                }.onFailure {
                    onFailed?.invoke("$it")
                }
            }
        }
    }

    var doStopClient = false
    fun stopClient() {
        doStopClient = true
        kotlin.runCatching {
            client?.getOutputStream()?.close()
        }
        kotlin.runCatching {
            client?.getInputStream()?.close()
        }
        kotlin.runCatching {
            client?.close()
        }
    }
    private fun stopServer() {
        serverRunning = false

        // 断开所有客户端
        mapIpClient.forEach {
            kotlin.runCatching {
                it.value.close()
            }
        }
        // 断开服务端
        kotlin.runCatching {
            serverSocket?.close()
        }
    }

    fun localIpAddress():String {
        val networkInterfaces = NetworkInterface.getNetworkInterfaces()
        while (networkInterfaces.hasMoreElements()) {
            val element = networkInterfaces.nextElement()
            val inetAddresses = element.inetAddresses
            while (inetAddresses.hasMoreElements()) {
                val element2 = inetAddresses.nextElement()
                if (!element2.isLoopbackAddress && element2 is Inet4Address) {
                    return element2.hostAddress
                }
            }
        }
        return ""
    }

    fun getIpAddressLastDigit():String {
        val ip = localIpAddress()
        return if (ip.length > 10) ip.substring(ip.lastIndexOf(".") + 1) else ip
    }

    private fun getIpAddressPrefix():String {
        val ip = localIpAddress()
        return if (ip.length > 10) ip.substring(0,ip.lastIndexOf(".")) else ip
    }

    private fun notifyListener(eachCallback:(TcpMessageListener)->Unit) {
        val iter = listeners.iterator()
        while (iter.hasNext()) {
            eachCallback.invoke(iter.next())
        }
    }

    fun release() {
        clearCached()
        stopServer()
        threadPool.shutdownNow()
    }

    private fun clearCached() {
        mapIpClient.clear()
        mapIpContent.clear()
        mapIpName.clear()
    }

    fun log(msg:String) {
        Log.d(TAG,msg)
    }
}

object TcpMessageParser {
    fun parse(message:String, onNameAccept:(String)->Unit, onMessageAccept:(String)->Unit) {
        val customModelBase = Gson().fromJson(message, CustomModelBase::class.java)
        when (customModelBase.type) {
            CustomModelBase.TYPE_NAME-> {
                onNameAccept.invoke(customModelBase.value?:"")
            }
            CustomModelBase.TYPE_MSG-> {
                onMessageAccept.invoke(customModelBase.value?:"")
            }
            else-> {

            }
        }
    }
}
interface TcpMessageListener {
    fun onServerStart(address: String, port:Int)
    fun onClientAccept(address:String)
    fun onClientClose(address:String)
    fun onMessageReceive(ip:String, message:String)
    fun onNameAccept(address: String, name:String)
    fun onConnectFailed(address: String, error:String)
    fun onConnectSuccess(address: String)
}